Skip to content

Atlas-Bounty: Implement Private Earnings Dashboard (/me/earnings) - #419

Merged
0xdevcollins merged 4 commits into
boundlessfi:mainfrom
Fankouzu:main
Feb 28, 2026
Merged

Atlas-Bounty: Implement Private Earnings Dashboard (/me/earnings)#419
0xdevcollins merged 4 commits into
boundlessfi:mainfrom
Fankouzu:main

Conversation

@Fankouzu

@Fankouzu Fankouzu commented Feb 28, 2026

Copy link
Copy Markdown
Contributor

Overview

Implemented a comprehensive Private Earnings Dashboard as requested in issue #390. This page allows users to track their platform income, visualize earnings by source, and claim rewards.

Key Deliverables

  • Route: Created with a responsive UI.
  • Sidebar Integration: Added "Earnings" link to .
  • API Layer: Implemented structured API calls in .
  • UI Components:
    • Summary cards (Total, Pending, Completed).
    • Categorized breakdown (Hackathons, Grants, etc.).
    • Recent activity feed with functional "Claim" actions.
  • Standards: 100% type-safe (no any), Framer Motion animations, and shadcn/ui adherence.

Payout Information

Address: 0xa5F1e2596DC1e878a6a039f41330d9A97c771bE9

Summary by CodeRabbit

  • New Features
    • Earnings dashboard with summary cards for Total Earned, Pending, and Completed withdrawals
    • Source breakdown (Hackathons, Grants, Crowdfunding, Bounties)
    • Recent activity feed showing individual earnings, dates, and amounts
    • New Earnings item added to the main navigation for quick access

@vercel

vercel Bot commented Feb 28, 2026

Copy link
Copy Markdown

Someone is attempting to deploy a commit to the Threadflow Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Feb 28, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9461e9b and 50ddce6.

📒 Files selected for processing (2)
  • app/me/earnings/page.tsx
  • lib/api/user/earnings.ts

📝 Walkthrough

Walkthrough

Adds a new client-side Earnings dashboard page, a user-earnings API module (fetch + claim), and a sidebar navigation entry linking to /me/earnings.

Changes

Cohort / File(s) Summary
Earnings Page
app/me/earnings/page.tsx
New client React page (default export). Implements data fetching via getUserEarnings, local loading/claim state, summary cards, source breakdown, recent activity list, skeleton UI, toasts, and claim flow using claimEarning.
API: User Earnings
lib/api/user/earnings.ts
New API module introducing types (EarningActivity, EarningsData, request/response types) and functions getUserEarnings() (GET /user/earnings) and claimEarning() (POST /user/earnings/claim).
Navigation / Sidebar
components/app-sidebar.tsx
Added Earnings to main navigation with IconCurrencyDollar, linking to /me/earnings alongside existing items.

Sequence Diagram

sequenceDiagram
    participant User
    participant Page as EarningsPage
    participant API as API Layer
    participant Backend as Backend

    rect rgba(100,150,200,0.5)
    Note over User,Backend: Initial data fetch on mount
    User->>Page: Open /me/earnings
    Page->>API: getUserEarnings()
    API->>Backend: GET /user/earnings
    Backend-->>API: EarningsData
    API-->>Page: GetEarningsResponse
    Page->>Page: Store data, stop loading
    Page-->>User: Render dashboard
    end

    rect rgba(150,100,200,0.5)
    Note over User,Backend: Claim earning flow
    User->>Page: Click Claim button
    Page->>Page: Set claimingId (in-flight)
    Page->>API: claimEarning(activityId)
    API->>Backend: POST /user/earnings/claim
    Backend-->>API: ClaimEarningResponse
    API-->>Page: Success
    Page->>Page: Show toast, clear claimingId
    Page->>API: getUserEarnings()
    API->>Backend: GET /user/earnings
    Backend-->>API: Updated EarningsData
    API-->>Page: Updated GetEarningsResponse
    Page->>Page: Refresh UI
    Page-->>User: Updated dashboard
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Possibly related issues

Poem

🐰 I hopped to the sidebar, a shiny new page,
Cards of carrots and coins on each little stage,
Click to claim, fetch to see — numbers gleam bright,
I rabbit-hop home with my earnings tonight,
Hooray for rewards and a well-tracked delight!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: implementing a Private Earnings Dashboard at /me/earnings route, which aligns with the PR's core objective and all included changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (4)
lib/api/user/earnings.ts (2)

55-58: Add generic type parameter for type-safe API response.

Same issue as getUserEarnings - pass the expected response type to api.post<T>().

♻️ Proposed fix
 export const claimEarning = async (data: ClaimEarningRequest): Promise<ClaimEarningResponse> => {
-  const res = await api.post('/user/earnings/claim', data);
+  const res = await api.post<ClaimEarningResponse>('/user/earnings/claim', data);
   return res.data;
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/api/user/earnings.ts` around lines 55 - 58, The API call in claimEarning
currently uses api.post without a generic result type, so make the request
type-safe by passing the expected response type to api.post (use
ClaimEarningResponse as the generic) when calling api.post in the claimEarning
function; update the call to api.post<ClaimEarningResponse>('
/user/earnings/claim', data) so res.data is correctly typed for
ClaimEarningResponse while leaving the function signature intact.

47-50: Add generic type parameter for type-safe API response.

The api.get<T>() method accepts a generic type parameter to properly type the response. Without it, res.data is typed as unknown.

♻️ Proposed fix
 export const getUserEarnings = async (): Promise<GetEarningsResponse> => {
-  const res = await api.get('/user/earnings');
+  const res = await api.get<EarningsData>('/user/earnings');
   return res.data;
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/api/user/earnings.ts` around lines 47 - 50, The API call in
getUserEarnings returns untyped data; call api.get with the generic type
parameter so the response is type-safe (use GetEarningsResponse as the generic
for api.get), then return res.data which will now be correctly typed; update the
api.get call inside function getUserEarnings to
api.get<GetEarningsResponse>(...) so res.data is inferred as
GetEarningsResponse.
app/me/earnings/page.tsx (2)

22-25: Consider using const arrow function per coding guidelines.

The coding guidelines prefer const arrow functions with explicit type annotations. However, function declarations for page components are common in Next.js, so this is a minor stylistic preference.

♻️ Optional refactor
-export default function EarningsPage() {
-  const [loading, setLoading] = useState(true);
-  const [data, setData] = useState<EarningsData | null>(null);
-  const [claimingId, setClaimingId] = useState<string | null>(null);
+const EarningsPage: React.FC = () => {
+  const [loading, setLoading] = useState(true);
+  const [data, setData] = useState<EarningsData | null>(null);
+  const [claimingId, setClaimingId] = useState<string | null>(null);

And at the end of the component:

-}
+};
+
+export default EarningsPage;

As per coding guidelines: "Prefer const arrow functions with explicit type annotations over function declarations".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/me/earnings/page.tsx` around lines 22 - 25, The component is declared as
a function declaration (EarningsPage); change it to a const arrow function with
an explicit type annotation per guidelines: replace the `export default function
EarningsPage()` declaration with a `const`-declared arrow component (e.g.,
`const EarningsPage: React.FC` or appropriate Next.js page type) and export it
as default; keep the existing state hooks (loading, data, claimingId) and
implementation intact while updating the declaration and export style.

56-57: Add error logging for consistency with fetchData.

The fetchData function logs errors to console before showing a toast (line 35), but handleClaim only shows a toast. Adding console.error would help with debugging.

♻️ Proposed fix
     } catch (error) {
+      console.error('Failed to claim earning:', error);
       toast.error('An error occurred during claiming');
     } finally {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/me/earnings/page.tsx` around lines 56 - 57, In the catch block of
handleClaim, add a console.error call to log the caught error (similar to
fetchData) before calling toast.error so errors are recorded for debugging;
locate the handleClaim function and update its catch(error) handler to
console.error(error) (or include context like "handleClaim error") then show the
toast.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/me/earnings/page.tsx`:
- Around line 51-52: The refresh logic currently sets state based only on
updated.data; update it to mirror the initial fetch by checking both
updated.success and updated.data before calling setData (i.e., replace the
single `if (updated.data)` with a check for `updated.success && updated.data`)
so getUserEarnings results are validated consistently when using setData.
- Around line 180-182: The Badge usage currently passes a non-existent 'success'
variant; update the conditional in the Badge component rendering (see Badge and
activity.status) to use a supported variant such as 'default' for completed
items (e.g., change the ternary that yields 'success' to 'default', keeping
'secondary' for pending and 'default' for the fallback).

In `@lib/api/user/earnings.ts`:
- Around line 1-2: The import is using a default import for api but the module
exports it as a named export; update the import to use a named import (change
the import of api to use curly braces) so the symbol api resolves correctly when
used in this file (the ApiResponse import can remain unchanged).

---

Nitpick comments:
In `@app/me/earnings/page.tsx`:
- Around line 22-25: The component is declared as a function declaration
(EarningsPage); change it to a const arrow function with an explicit type
annotation per guidelines: replace the `export default function EarningsPage()`
declaration with a `const`-declared arrow component (e.g., `const EarningsPage:
React.FC` or appropriate Next.js page type) and export it as default; keep the
existing state hooks (loading, data, claimingId) and implementation intact while
updating the declaration and export style.
- Around line 56-57: In the catch block of handleClaim, add a console.error call
to log the caught error (similar to fetchData) before calling toast.error so
errors are recorded for debugging; locate the handleClaim function and update
its catch(error) handler to console.error(error) (or include context like
"handleClaim error") then show the toast.

In `@lib/api/user/earnings.ts`:
- Around line 55-58: The API call in claimEarning currently uses api.post
without a generic result type, so make the request type-safe by passing the
expected response type to api.post (use ClaimEarningResponse as the generic)
when calling api.post in the claimEarning function; update the call to
api.post<ClaimEarningResponse>(' /user/earnings/claim', data) so res.data is
correctly typed for ClaimEarningResponse while leaving the function signature
intact.
- Around line 47-50: The API call in getUserEarnings returns untyped data; call
api.get with the generic type parameter so the response is type-safe (use
GetEarningsResponse as the generic for api.get), then return res.data which will
now be correctly typed; update the api.get call inside function getUserEarnings
to api.get<GetEarningsResponse>(...) so res.data is inferred as
GetEarningsResponse.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c584ac3 and 74c438f.

📒 Files selected for processing (3)
  • app/me/earnings/page.tsx
  • components/app-sidebar.tsx
  • lib/api/user/earnings.ts

Comment thread app/me/earnings/page.tsx Outdated
Comment thread app/me/earnings/page.tsx Outdated
Comment thread lib/api/user/earnings.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
app/me/earnings/page.tsx (2)

154-167: Prefer const arrow functions with explicit type annotations.

The helper components (SummaryCard, BreakdownItem, ActivityItem, EarningsSkeleton) use function declarations. Per coding guidelines, prefer const arrow functions with explicit type annotations.

♻️ Example refactor for SummaryCard
-function SummaryCard({ title, value, icon, description }: { title: string, value: string, icon: React.ReactNode, description: string }) {
-  return (
+interface SummaryCardProps {
+  title: string;
+  value: string;
+  icon: React.ReactNode;
+  description: string;
+}
+
+const SummaryCard: React.FC<SummaryCardProps> = ({ title, value, icon, description }) => (
     <Card>
       <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
         <CardTitle className="text-sm font-medium">{title}</CardTitle>
         {icon}
       </CardHeader>
       <CardContent>
         <div className="text-2xl font-bold">{value}</div>
         <p className="text-xs text-muted-foreground mt-1">{description}</p>
       </CardContent>
     </Card>
-  );
-}
+);

Apply the same pattern to BreakdownItem, ActivityItem, and EarningsSkeleton. As per coding guidelines: "Prefer const arrow functions with explicit type annotations over function declarations."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/me/earnings/page.tsx` around lines 154 - 167, Replace the function
declarations with const arrow-function components and add explicit type
annotations for props: convert SummaryCard, BreakdownItem, ActivityItem, and
EarningsSkeleton into const SummaryCard: React.FC<{ title: string; value:
string; icon: React.ReactNode; description: string }> = ({ title, value, icon,
description }) => { ... } (and similarly define precise prop types for
BreakdownItem and ActivityItem and type EarningsSkeleton as React.FC or a
specific props type if needed); update the component definitions and exports to
use these const arrow functions so they follow the codebase guideline.

76-76: Consider styling the empty state for consistency.

The "No data found" fallback is unstyled, while other states (loading skeleton, empty activities list at line 135) have proper styling. Consider matching the styling for a consistent UX.

♻️ Suggested improvement
-  if (!data) return <div>No data found.</div>;
+  if (!data) {
+    return (
+      <div className="container mx-auto py-8">
+        <p className="text-center text-muted-foreground py-8">No earnings data found.</p>
+      </div>
+    );
+  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/me/earnings/page.tsx` at line 76, Replace the unstyled fallback `if
(!data) return <div>No data found.</div>;` with the same styled empty-state
markup/component used for the activities empty state (the JSX rendered when
activities are empty) so the messaging matches loading/empty UI; find the empty
activities JSX (the block that displays the empty activities list) and reuse its
container, classes or dedicated EmptyState component, replacing the plain "No
data found." text so the empty state looks consistent with other states.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@app/me/earnings/page.tsx`:
- Around line 154-167: Replace the function declarations with const
arrow-function components and add explicit type annotations for props: convert
SummaryCard, BreakdownItem, ActivityItem, and EarningsSkeleton into const
SummaryCard: React.FC<{ title: string; value: string; icon: React.ReactNode;
description: string }> = ({ title, value, icon, description }) => { ... } (and
similarly define precise prop types for BreakdownItem and ActivityItem and type
EarningsSkeleton as React.FC or a specific props type if needed); update the
component definitions and exports to use these const arrow functions so they
follow the codebase guideline.
- Line 76: Replace the unstyled fallback `if (!data) return <div>No data
found.</div>;` with the same styled empty-state markup/component used for the
activities empty state (the JSX rendered when activities are empty) so the
messaging matches loading/empty UI; find the empty activities JSX (the block
that displays the empty activities list) and reuse its container, classes or
dedicated EmptyState component, replacing the plain "No data found." text so the
empty state looks consistent with other states.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 74c438f and 9461e9b.

📒 Files selected for processing (2)
  • app/me/earnings/page.tsx
  • lib/api/user/earnings.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/api/user/earnings.ts

@0xdevcollins

Copy link
Copy Markdown
Collaborator

LGTM 🚀

… claim functionality, and update earnings data structure for improved clarity and performance.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants